package com.ljc;

/**
 * @author clj
 * @date 2022/12/8
 * @desc 有效回文串
 * A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters,
 * it reads the same forward and backward. Alphanumeric characters include letters and numbers.
 *
 * Given a string s, return true if it is a palindrome, or false otherwise.
 *
 * Example 1:
 * Input: s = "A man, a plan, a canal: Panama"
 * Output: true
 * Explanation: "amanaplanacanalpanama" is a palindrome.
 *
 * Example 2:
 * Input: s = "race a car"
 * Output: false
 * Explanation: "raceacar" is not a palindrome.
 *
 * Example 3:
 * Input: s = " "
 * Output: true
 * Explanation: s is an empty string "" after removing non-alphanumeric characters.
 * Since an empty string reads the same forward and backward, it is a palindrome.
 */
public class E125ValidPalindrome {
    public static void main(String[] args) {
        String inputStr = "A man, a plan, a canal: Panama";
        System.out.println(isPalindrome1(inputStr));
        System.out.println(isPalindrome2(inputStr));
        System.out.println(isPalindrome3(inputStr));
    }


    /**
     * 筛选 + 判断
     * 最简单的方法是对字符串 s 进行一次遍历，并将其中的字母和数字字符进行保留，放在另一个字符串 sgood 中。这样我们只需要判断 sgood 是否是一个普通的回文串即可。
     *
     * 判断的方法有两种。第一种是使用语言中的字符串翻转 API 得到 sgood 的逆序字符串 sgood_rev，只要这两个字符串相同，那么 sgood 就是回文串。
     * @param s
     * @return
     */
    public static boolean isPalindrome1(String s) {
        StringBuffer sgood = trimStr(s);
        StringBuffer sgoodRev = new StringBuffer(sgood).reverse();
        return sgood.toString().equals(sgoodRev.toString());
    }

    /**
     * 第二种是使用双指针。初始时，左右指针分别指向 sgood 的两侧，随后我们不断地将这两个指针相向移动，每次移动一步，并判断这两个指针指向的字符是否相同。
     * 当这两个指针相遇时，就说明 sgood 时回文串。
     * @param inputStr
     * @return
     */
    public static boolean isPalindrome2(String inputStr) {
        inputStr = trimStr(inputStr).toString();
        int backwardIndex = inputStr.length()-1;
        for (int i = 0; i <= backwardIndex; i++) {
            if (inputStr.charAt(i) != inputStr.charAt(backwardIndex)) {
                return false;
            }else {
                backwardIndex -= 1;
            }
        }
        return true;
    }

    public static StringBuffer trimStr(String s) {
        StringBuffer sgood = new StringBuffer();
        int length = s.length();
        for (int i = 0; i < length; i++) {
            char ch = s.charAt(i);
            //如果是数字或字母，则保留；否则，去掉不保留
            if (Character.isLetterOrDigit(ch)) {
                sgood.append(Character.toLowerCase(ch));
            }
        }
        return sgood;
    }

    public static String trimString(String s) {
        StringBuffer str = new StringBuffer();
        int length = s.length();
        for (int i = 0; i < length; i++) {
            char ch = s.charAt(i);
            if (Character.isLetterOrDigit(ch)) {
                str.append(Character.toLowerCase(ch));
            }
        }
        return str.toString();
    }


    /**
     * 在原字符串上直接判断
     * 我们可以对方法一中第二种判断回文串的方法进行优化，就可以得到只使用 O(1) 空间的算法。
     *
     * 我们直接在原字符串 s 上使用双指针。在移动任意一个指针时，需要不断地向另一指针的方向移动，直到遇到一个字母或数字字符，或者两指针重合为止。
     * 也就是说，我们每次将指针移到下一个字母字符或数字字符，再判断这两个指针指向的字符是否相同。
     * @param s
     * @return
     */
    public static boolean isPalindrome3(String s) {
        int n = s.length();
        int left = 0, right = n - 1;
        while (left < right) {
            while (left < right && !Character.isLetterOrDigit(s.charAt(left))) {
                ++left;
            }
            while (left < right && !Character.isLetterOrDigit(s.charAt(right))) {
                --right;
            }
            if (left < right) {
                if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
                    return false;
                }
                ++left;
                --right;
            }
        }
        return true;
    }

}
